/**
 * @file opencvWindow.hpp
 * @author SDK_TEAM
 * @brief
 * @version 0.1
 * @date 2022-11-10
 *
 * Copyright:
 * © 2018 北京灵汐科技有限公司 版权所有。
 * 注意：以下内容均为北京灵汐科技有限公司原创，未经本公司允许，不得转载，否则将视为侵权；对于不遵守此声明或者其他违法使用以下内容者，本公司依法保留追究权。
 * © 2018 Lynxi Technologies Co., Ltd. All rights reserved.
 * NOTICE: All information contained here is, and remains the property of Lynxi.
 * This file can not be copied or distributed without the permission of Lynxi
 * Technologies Co., Ltd.
 *
 */

#pragma once

#include <memory>
#include <mutex>
#include <vector>
#ifdef HAVE_OPENCV
#include <opencv2/opencv.hpp>
#endif
#include <lyn_api.h>
#include "blockQueue.hpp"

// Opencv数据结构体
typedef struct _tagOpencvData {
#ifdef HAVE_OPENCV
  cv::Mat rgbMat;
#endif
  std::string windowName;  // 通道 Title
} OPENCV_DATA_T;

#define OPENCV_DATA_T_PTR std::shared_ptr<OPENCV_DATA_T>

#define VIDEO_SIZE (1920 * 1080 * 3 / 2)

class OpencvWindowManager {
 public:
  static OpencvWindowManager *GetInstance() {
    static OpencvWindowManager s_oInstance;
    return &s_oInstance;
  }

  void SetWindowSize(std::string &windowName, int hight, int width) {
    m_windowHight = hight;
    m_windowWidth = width;
  }

  void SetWindowNames(std::vector<std::string> &vecName) {
    std::unique_lock<std::mutex> locker(m_mutex);
    m_close = false;
    m_activeChannel = vecName.size();
#ifdef HAVE_OPENCV
    for (auto &name : vecName) {
      cv::namedWindow(name);
      // cv::startWindowThread();
    }
#endif
  }

  void SetWindowName(std::string name) {
    std::unique_lock<std::mutex> locker(m_mutex);
    m_close = false;
    m_activeChannel = 1;
#ifdef HAVE_OPENCV
    cv::namedWindow(name);
#endif
  }

  /**
   * @brief 处理函数，每隔一个固定时间去渲染 m_showPtr 中的数据，直至窗口关闭
   *
   * @return 无
   */
  void Process() {
    m_close = false;
#ifdef HAVE_OPENCV
    while (!m_start) {
      std::this_thread::sleep_for(std::chrono::milliseconds(1));
    }
    cv::Mat rgbImg(m_windowHight, m_windowWidth, CV_8UC3);
#endif
    while (!m_close) {
#ifdef HAVE_OPENCV
      {
        std::unique_lock<std::mutex> locker(m_mutex);
        m_showPtr.swap(m_buffData);
      }
      cv::Mat yuvImg(m_windowHight * 3 / 2, m_windowWidth, CV_8UC1,
                     m_showPtr.get());
      cv::cvtColor(yuvImg, rgbImg, cv::COLOR_YUV2BGR_NV12);
      cv::imshow(m_windowName, rgbImg);
      cv::waitKey(100);
#else
      std::this_thread::sleep_for(std::chrono::milliseconds(100));
#endif
    }
  }

  /**
   * @brief 将 Device 侧数据拷贝至 Host 侧，并更新 m_buffData 中的数据
   *
   * @param  frame
   * @param  windowName
   */
  void ShowDevicesData(lynFrame_t *frame, std::string &windowName) {
#ifdef HAVE_OPENCV

    lynMemcpy(m_writePtr.get(), frame->data, frame->size, ServerToClient);
    std::unique_lock<std::mutex> locker(m_mutex);
    m_buffData.swap(m_writePtr);
    m_start = true;
    m_windowName = windowName;
#endif
  }

  void ShowImage(std::string &windowName, lynFrame_t *frame, int width,
                 int height, int showTime) {
#ifdef HAVE_OPENCV
    uint8_t *hostBuf = (uint8_t *)malloc(frame->size * sizeof(uint8_t));
    lynMemcpy(hostBuf, frame->data, frame->size, ServerToClient);
    cv::Mat yuvImg(height * 3 / 2, width, CV_8UC1, hostBuf);
    cv::Mat rgbImg(height, width, CV_8UC3);
    cv::cvtColor(yuvImg, rgbImg, cv::COLOR_YUV2BGR_NV12);
    cv::imshow(windowName, rgbImg);
    if (showTime > 0) {
      cv::waitKey(showTime);
    }
    free(hostBuf);
#endif
  }

  void Close(std::string &windowName) {
    std::unique_lock<std::mutex> locker(m_mutex);
    --m_activeChannel;
    if (m_activeChannel == 0) {
      m_close = true;
    }
  }

 private:
  OpencvWindowManager()
      : m_showPtr(new uint8_t[VIDEO_SIZE], [](uint8_t *p) { delete[] p; }),
        m_writePtr(new uint8_t[VIDEO_SIZE], [](uint8_t *p) { delete[] p; }),
        m_buffData(new uint8_t[VIDEO_SIZE], [](uint8_t *p) { delete[] p; }) {}

  ~OpencvWindowManager() {}

  OpencvWindowManager(OpencvWindowManager &) = delete;
  OpencvWindowManager &operator=(OpencvWindowManager) = delete;

 private:
  std::shared_ptr<uint8_t> m_showPtr;
  std::shared_ptr<uint8_t> m_writePtr;
  std::shared_ptr<uint8_t> m_buffData;
  std::mutex m_mutex;
  bool m_close;
  bool m_start = false;
  std::string m_windowName;
  int m_activeChannel;
  int m_windowHight = 1080;
  int m_windowWidth = 1920;
};
